package com.jjtm.common.core.utils;

import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author wangbaofeng
 */
@Service
public class RedisUtils {

	@Resource
	private RedisTemplate<String, Object> redisTemplate;

	/**
	 * 不设置过期时间
	 */
	private final static long NOT_EXPIRE = -1;

	//批量查询
	public List<Object> mGet(List<String> key){
		return redisTemplate.opsForValue().multiGet(key);
	}

	/**
	 * @param key
	 * @param value
	 * @param expire 过期时间 单位：天
	 */
	public void set(String key, Object value, long expire) {
		if (expire != NOT_EXPIRE) {
			redisTemplate.opsForValue().set(key, value, expire, TimeUnit.DAYS);
		} else {
			redisTemplate.opsForValue().set(key, value);
		}
	}

	public void set(String key, Object value, long expire, TimeUnit unit) {
		if (expire != NOT_EXPIRE) {
			redisTemplate.opsForValue().set(key, value, expire, unit);
		} else {
			redisTemplate.opsForValue().set(key, value);
		}
	}

	public void listRightPush(String key, Object value) {
		redisTemplate.opsForList().rightPush(key, value);
	}

	public void setList (String key, List<Object> list) {
		redisTemplate.opsForList().rightPushAll(key, list);
	}

	public void listLeftPush(String key, Object value) {
		redisTemplate.opsForList().leftPush(key, value);
	}

	public Long listRemove(String key, Object value) {
		return redisTemplate.opsForList().remove(key, 0, value);
	}

	public List<?> getList(String key, Long start, Long end) {
		return redisTemplate.opsForList().range(key, start, end);
	}

	public void setToken(String key, Object value, long expire) {
		redisTemplate.opsForValue().set(key, value, expire, TimeUnit.SECONDS);
	}

	public void setHash(String key, String hashKey, Object value) {
		redisTemplate.opsForHash().put(key, hashKey, value);
	}

	public List<Object> getHash(String key) {
		return redisTemplate.opsForHash().values(key);
	}

	public Set<Object> getHashKeys(String key) {
		return redisTemplate.opsForHash().keys(key);
	}

	public Long getHashSize(String key) {
		return redisTemplate.opsForHash().size(key);
	}

	public Long deleteHash(String key, Object... hashKeys) {
		return redisTemplate.opsForHash().delete(key, hashKeys);
	}

	public Set<String> setKeys(String keys) {
		return redisTemplate.keys(keys + "*");
	}

	public void delete(String key) {
		redisTemplate.delete(key);
	}

	public void delete(Set<String> keys) {
		redisTemplate.delete(keys);
	}

	public void deleteKeys(String pattern){
		redisTemplate.delete(Objects.requireNonNull(redisTemplate.keys(pattern)));
	}

	public Object get(String key) {
		try {
			// 先获取缓存中的
			return redisTemplate.opsForValue().get(key);
		} catch (Exception e) {
			RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory());
			return null;
		}

	}
	
	public boolean hasKey(String key) {
		return redisTemplate.hasKey(key);
	}

	/**
	 * set长度
	 * 
	 * @param key
	 * @return
	 */
	public Long setSize(String key) {
		return redisTemplate.opsForSet().size(key);
	}

	/**
	 * hash长度
	 * 
	 * @param key
	 * @return
	 */
	public Long hashSize(String key) {
		return redisTemplate.opsForHash().size(key);
	}

	/**
	 * hash取值
	 * 
	 * @param key
	 * @return
	 */
	public List<Object> getHashValue(String key) {
		return redisTemplate.opsForHash().values(key);
	}

	/**
	 * 正则表达式取出所有匹配key
	 * 
	 * @param pattern
	 * @return
	 */
	public Set<String> keys(String pattern) {
		return redisTemplate.keys(pattern);
	}

	/**
	 * 查询是否含有key值
	 * 
	 * @param key
	 * @return
	 * @throws IOException
	 */
	@SuppressWarnings("unchecked")
	public boolean scan(String key) throws IOException {
		ScanOptions options = ScanOptions.scanOptions()
				// 这里指定每次扫描key的数量(很多博客瞎说要指定Integer.MAX_VALUE，这样的话跟keys有什么区别？)
				.count(10000).match(key).build();
		RedisSerializer<String> redisSerializer = (RedisSerializer<String>) redisTemplate.getKeySerializer();
		Cursor<?> cursor = redisTemplate.executeWithStickyConnection(redisConnection -> new ConvertingCursor<>(redisConnection.scan(options), redisSerializer::deserialize));
		List<String> result = new ArrayList<>();
		while (cursor.hasNext()) {
			result.add(cursor.next().toString());
		}
		cursor.close();
		return !CollectionUtils.isEmpty(result);
	}
	
	@SuppressWarnings("unchecked")
	public Integer scanSize(String key) throws IOException {
		ScanOptions options = ScanOptions.scanOptions()
				// 这里指定每次扫描key的数量(很多博客瞎说要指定Integer.MAX_VALUE，这样的话跟keys有什么区别？)
				.count(10000).match(key).build();
		RedisSerializer<String> redisSerializer = (RedisSerializer<String>) redisTemplate.getKeySerializer();
		Cursor<?> cursor = redisTemplate.executeWithStickyConnection(redisConnection -> new ConvertingCursor<>(redisConnection.scan(options), redisSerializer::deserialize));
		List<String> result = new ArrayList<>();
		while (cursor.hasNext()) {
			result.add(cursor.next().toString());
		}
		cursor.close();
		return result.size();
	}

	/**
	 * 创建锁
	 *
	 * @param key   锁的Key
	 * @param value 值(随便写毫无意义)
	 * @return
	 */
	public boolean lock(String key, Object value) {
		// 尝试获取锁
		Boolean boo = redisTemplate.opsForValue().setIfAbsent(key, value);
		// 判断结果
		return boo != null && boo;
	}

	public void unLock(String key, Object value) {
		String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
		DefaultRedisScript<Long> defaultRedisScript = new DefaultRedisScript<Long>();
		defaultRedisScript.setScriptText(script);
		defaultRedisScript.setResultType(Long.class);
		// 执行 脚本 删除 key ,必须使用lua 脚本实现 保证原子性
		redisTemplate.execute(defaultRedisScript, Collections.singletonList(key), value);
	}


	public Map<Object,Object> entries(String key){
		return redisTemplate.opsForHash().entries(key);
	}
}
